/**
 * 性能测试工具类
 * 用于测量和记录应用程序的性能指标
 * 包括页面加载时间、操作响应时间、资源加载性能等
 */

// 定义性能记录接口，用于存储单次性能测量的数据
interface PerformanceRecord {
  name: string;                  // 操作名称，用于标识不同的性能测量点
  startTime: number;             // 操作开始时间（performance.now()的返回值）
  endTime: number;               // 操作结束时间（performance.now()的返回值）
  duration: number;              // 操作持续时间（毫秒），等于endTime - startTime
  metadata?: Record<string, any>; // 可选的元数据，可以包含任何与此次测量相关的额外信息
}

// 定义性能测试工具配置选项接口
interface PerformanceTestOptions {
  autoReport?: boolean;          // 是否自动在控制台报告性能问题，默认为true
  reportThreshold?: number;      // 性能警告阈值（毫秒），超过此阈值的操作将被标记为性能问题
}

// 性能测试工具类，提供一套完整的API用于测量和分析应用程序性能
class PerformanceTest {
  private records: PerformanceRecord[] = [];                // 存储所有已完成的性能测量记录
  private activeTimers: Map<string, number> = new Map();    // 存储当前活动的计时器，键为计时器名称，值为开始时间
  private options: PerformanceTestOptions = {               // 工具类配置选项
    autoReport: true,                                       // 默认启用自动报告
    reportThreshold: 500,                                   // 默认性能警告阈值为500毫秒
  };

  // 构造函数，创建性能测试工具实例
  constructor(options?: PerformanceTestOptions) {
    // 如果提供了选项，则合并用户提供的选项与默认选项
    if (options) {
      this.options = { ...this.options, ...options };       // 使用展开运算符合并对象
    }

    // 初始化时自动记录页面加载性能指标
    this.recordPageLoadMetrics();                           // 调用方法记录页面加载性能
  }

  /**
   * 记录页面加载性能指标
   * 使用Performance API获取页面加载相关的时间数据
   */
  private recordPageLoadMetrics(): void {
    // 检查浏览器是否支持Performance API的timing接口
    if (window.performance && window.performance.timing) {   // 确保浏览器支持Performance API
      // 获取timing对象（注意：此API已被弃用，但在许多浏览器中仍然可用）
      const timing = window.performance.timing;              // 获取性能计时对象

      // 页面加载完成后计算各项指标
      window.addEventListener('load', () => {                // 添加页面加载完成事件监听器
        // 计算关键性能指标
        const pageLoadTime = timing.loadEventEnd - timing.navigationStart;  // 计算整个页面加载时间
        const domReadyTime = timing.domComplete - timing.domLoading;        // 计算DOM准备时间
        const networkLatency = timing.responseEnd - timing.requestStart;     // 计算网络延迟时间

        // 记录页面加载性能
        this.records.push({                                  // 将性能记录添加到records数组
          name: 'pageLoad',                                  // 记录名称为'pageLoad'
          startTime: timing.navigationStart,                 // 开始时间为导航开始时间
          endTime: timing.loadEventEnd,                      // 结束时间为加载完成时间
          duration: pageLoadTime,                            // 持续时间为计算出的页面加载时间
          metadata: {                                        // 附加元数据
            domReadyTime,                                    // DOM准备时间
            networkLatency,                                  // 网络延迟
            domContentLoaded: timing.domContentLoadedEventEnd - timing.navigationStart, // DOM内容加载时间
            firstPaint: this.getFirstPaintTime(),            // 首次绘制时间，通过调用getFirstPaintTime方法获取
          }
        });

        // 如果启用了自动报告且页面加载时间超过阈值，则在控制台发出警告
        if (this.options.autoReport && pageLoadTime > this.options.reportThreshold!) { // 检查是否超过阈值
          console.warn(`页面加载时间(${pageLoadTime}ms)超过阈值(${this.options.reportThreshold}ms)`); // 输出警告信息
        }
      });
    }
  }

  /**
   * 获取首次绘制时间
   * 使用Performance API的paint timing获取首次绘制的时间点
   */
  private getFirstPaintTime(): number {
    // 获取performance对象
    const performance = window.performance;                  // 获取全局performance对象

    // 检查浏览器是否支持getEntriesByType方法
    if (performance && performance.getEntriesByType) {       // 确保浏览器支持getEntriesByType方法
      // 获取所有绘制相关的性能条目
      const paintMetrics = performance.getEntriesByType('paint'); // 获取所有paint类型的性能条目
      // 查找first-paint条目
      const firstPaint = paintMetrics.find(entry => entry.name === 'first-paint'); // 查找名为'first-paint'的条目

      // 如果找到first-paint条目，返回其开始时间
      if (firstPaint) {                                      // 如果找到了first-paint条目
        return firstPaint.startTime;                         // 返回其开始时间
      }
    }

    // 如果不支持或未找到，返回0
    return 0;                                                // 默认返回0
  }

  /**
   * 开始计时
   * 创建一个新的计时器并记录开始时间
   * @param name 计时器名称
   * @param metadata 附加信息
   */
  startTimer(name: string, metadata?: Record<string, any>): void {
    // 使用performance.now()获取当前时间作为开始时间
    const startTime = performance.now();                     // 获取高精度时间戳作为开始时间
    // 将计时器名称和开始时间存储在activeTimers映射中
    this.activeTimers.set(name, startTime);                  // 将计时器添加到活动计时器映射

    // 在控制台输出开始测量的日志
    console.log(`[性能测试] 开始测量: ${name}`);              // 输出开始测量的日志
  }

  /**
   * 结束计时并记录结果
   * 停止指定的计时器，计算持续时间并记录结果
   * @param name 计时器名称
   * @param additionalMetadata 附加信息
   * @returns 持续时间(毫秒)
   */
  endTimer(name: string, additionalMetadata?: Record<string, any>): number {
    // 使用performance.now()获取当前时间作为结束时间
    const endTime = performance.now();                       // 获取高精度时间戳作为结束时间

    // 检查计时器是否存在，如果不存在则发出警告并返回0
    if (!this.activeTimers.has(name)) {                      // 检查计时器是否存在
      console.warn(`[性能测试] 计时器 "${name}" 未启动`);      // 输出警告信息
      return 0;                                              // 返回0表示无效的计时
    }

    // 获取开始时间并计算持续时间
    const startTime = this.activeTimers.get(name)!;          // 获取开始时间，使用非空断言
    const duration = endTime - startTime;                    // 计算持续时间（毫秒）

    // 创建性能记录对象
    const record: PerformanceRecord = {                      // 创建新的性能记录
      name,                                                  // 操作名称
      startTime,                                             // 开始时间
      endTime,                                               // 结束时间
      duration,                                              // 持续时间
      metadata: additionalMetadata                           // 附加元数据
    };

    // 将记录添加到records数组中
    this.records.push(record);                               // 将记录添加到性能记录数组
    // 从activeTimers映射中删除此计时器
    this.activeTimers.delete(name);                          // 从活动计时器映射中移除此计时器

    // 在控制台输出测量结果
    console.log(`[性能测试] ${name}: ${duration.toFixed(2)}ms`); // 输出测量结果，保留2位小数

    // 如果启用了自动报告且持续时间超过阈值，则在控制台发出警告
    if (this.options.autoReport && duration > this.options.reportThreshold!) { // 检查是否超过阈值
      console.warn(`[性能测试] ${name} 操作耗时(${duration.toFixed(2)}ms)超过阈值(${this.options.reportThreshold}ms)`); // 输出警告信息
    }

    // 返回持续时间
    return duration;                                         // 返回计算出的持续时间
  }

  /**
   * 测量函数执行时间
   * 包装函数调用，自动记录其执行时间
   * @param name 操作名称
   * @param fn 要测量的函数
   * @param metadata 附加信息
   * @returns 函数执行结果
   */
  async measure<T>(name: string, fn: () => T | Promise<T>, metadata?: Record<string, any>): Promise<T> {
    // 开始计时
    this.startTimer(name, metadata);                         // 调用startTimer开始计时

    try {
      // 执行函数并等待结果
      const result = await fn();                             // 执行传入的函数并等待结果
      // 结束计时
      this.endTimer(name, metadata);                         // 调用endTimer结束计时
      // 返回函数结果
      return result;                                         // 返回函数执行结果
    } catch (error) {
      // 如果函数执行出错，记录错误信息并结束计时
      this.endTimer(name, { ...metadata, error: error instanceof Error ? error.message : String(error) }); // 结束计时并记录错误信息
      // 重新抛出错误
      throw error;                                           // 重新抛出错误，保持原始错误传播
    }
  }

  /**
   * 获取所有性能记录
   * 返回所有已完成的性能记录的副本
   */
  getRecords(): PerformanceRecord[] {
    // 返回records数组的副本，避免外部修改内部状态
    return [...this.records];                                // 使用展开运算符创建数组副本
  }

  /**
   * 获取特定操作的性能记录
   * 过滤出匹配指定名称的所有性能记录
   * @param name 操作名称
   */
  getRecordsByName(name: string): PerformanceRecord[] {
    // 过滤出匹配指定名称的记录
    return this.records.filter(record => record.name === name); // 使用filter方法过滤记录
  }

  /**
   * 计算特定操作的平均执行时间
   * 计算指定名称的所有操作的平均持续时间
   * @param name 操作名称
   */
  getAverageDuration(name: string): number {
    // 获取指定名称的所有记录
    const records = this.getRecordsByName(name);             // 获取指定名称的所有记录

    // 如果没有记录，返回0
    if (records.length === 0) {                              // 检查是否有记录
      return 0;                                              // 如果没有记录，返回0
    }

    // 计算总持续时间
    const totalDuration = records.reduce((sum, record) => sum + record.duration, 0); // 使用reduce计算总持续时间
    // 返回平均值（总时间除以记录数）
    return totalDuration / records.length;                   // 计算并返回平均值
  }

  /**
   * 生成性能报告
   * 汇总所有性能记录，按操作名称分组并计算统计数据
   */
  generateReport(): Record<string, any> {
    // 创建一个对象，用于按操作名称分组记录
    const groupedRecords: Record<string, PerformanceRecord[]> = {}; // 创建空对象用于分组

    // 将记录按名称分组
    this.records.forEach(record => {                         // 遍历所有记录
      // 如果该名称的分组不存在，则创建一个空数组
      if (!groupedRecords[record.name]) {                    // 检查分组是否存在
        groupedRecords[record.name] = [];                    // 如果不存在，创建空数组
      }
      // 将记录添加到对应的分组中
      groupedRecords[record.name].push(record);              // 将记录添加到对应分组
    });

    // 创建报告对象
    const report: Record<string, any> = {};                  // 创建空对象用于存储报告

    // 计算每组的统计数据
    Object.entries(groupedRecords).forEach(([name, records]) => { // 遍历所有分组
      // 提取所有持续时间
      const durations = records.map(r => r.duration);        // 提取所有记录的持续时间
      // 计算总时间
      const total = durations.reduce((sum, d) => sum + d, 0); // 计算总持续时间
      // 计算平均时间
      const avg = total / durations.length;                  // 计算平均持续时间
      // 找出最小值
      const min = Math.min(...durations);                    // 计算最小持续时间
      // 找出最大值
      const max = Math.max(...durations);                    // 计算最大持续时间

      // 将统计数据添加到报告中
      report[name] = {                                       // 为每个操作名称创建报告条目
        count: records.length,                               // 记录次数
        average: avg,                                        // 平均时间
        min,                                                 // 最小时间
        max,                                                 // 最大时间
        total,                                               // 总时间
        lastExecution: records[records.length - 1].duration  // 最后一次执行的时间
      };
    });

    // 返回报告对象
    return report;                                           // 返回生成的报告
  }

  /**
   * 清除所有性能记录
   * 重置性能测试工具的状态
   */
  clearRecords(): void {
    // 将records数组重置为空数组
    this.records = [];                                       // 清空记录数组
  }

  /**
   * 监控资源加载性能
   * 检查页面上加载的资源（如图片、脚本、样式表等）的加载时间
   */
  monitorResourceLoading(): void {
    // 检查浏览器是否支持getEntriesByType方法
    if (window.performance && window.performance.getEntriesByType) { // 确保浏览器支持getEntriesByType方法
      // 获取所有资源加载的性能条目
      const resourceEntries = window.performance.getEntriesByType('resource'); // 获取所有resource类型的性能条目

      // 检查每个资源的加载时间
      resourceEntries.forEach(entry => {                     // 遍历所有资源条目
        // 将entry转换为PerformanceResourceTiming类型
        const resource = entry as PerformanceResourceTiming; // 类型转换为PerformanceResourceTiming

        // 只关注加载慢的资源
        if (resource.duration > this.options.reportThreshold!) { // 检查资源加载时间是否超过阈值
          console.warn(`资源加载过慢: ${resource.name}, 耗时: ${resource.duration.toFixed(2)}ms`); // 输出警告信息
        }
      });
    }
  }
}

// 创建性能测试工具的单例实例，在整个应用中共享使用
const performanceTest = new PerformanceTest();               // 创建PerformanceTest类的实例

// 导出性能测试工具实例，可以在应用的任何地方导入和使用
export default performanceTest;                              // 导出单例实例
